﻿using System;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
using Kugar.Core.ExtMethod;
using Kugar.Web.Wechat.Enums;

namespace Kugar.Web.Wechat.MP
{
    internal static class MPOAuthHanderInfoCache
    {
        private static ConcurrentDictionary<string, IMPOAuthHandler> _cacheHanders = new ConcurrentDictionary<string, IMPOAuthHandler>();
        private static ConcurrentDictionary<string,IMPOAuthWithSchemeHandler> _cacheWithSchemeHandlers=new ConcurrentDictionary<string, IMPOAuthWithSchemeHandler>();

        private static ConcurrentDictionary<string,string> _cacheMethodHandlerName=new ConcurrentDictionary<string, string>(StringComparer.CurrentCultureIgnoreCase);
        private static ConcurrentDictionary<MemberInfo, CalcedActionOAuthHandlerInfo> _cacheCalcedActionHandlerInfo=new ConcurrentDictionary<MemberInfo, CalcedActionOAuthHandlerInfo>();

        public static IMPOAuthHandler GetMPHandler(string typeName)
        {
            return _cacheHanders.GetOrAdd(typeName, x =>
            {
                var type = AppDomain.CurrentDomain.GetAssemblies().SelectMany(y => y.GetTypes())
                    .Where(y => (y.Namespace + "." + y.Name) == typeName)
                    .FirstOrDefault();

                if (type == null)
                {
                    return null;
                }

                return (IMPOAuthHandler)Activator.CreateInstance(type);
            });
        }

        internal static void AddHanderWithScheme(string typeName, IMPOAuthWithSchemeHandler handler)
        {
            _cacheWithSchemeHandlers.AddOrUpdate(typeName, handler);
        }

        public static IMPOAuthWithSchemeHandler GetMPHandlerWithScheme(string typeName)
        {
            if (_cacheWithSchemeHandlers.TryGetValue(typeName, out var handler))
            {
                return handler;
            }
            else
            {
                return null;
            }
        }

        /// <summary>
        /// 获取经过计算统计后的action成员handler信息,因为可能出现action会再次使用WechatMPOAuthAttribute,并且不指定handlertype的情况,因此需要计算action向上的跟踪结果
        /// </summary>
        /// <param name="member"></param>
        /// <returns></returns>
        public static CalcedActionOAuthHandlerInfo GetMemberHandlerInfo(MemberInfo member)
        {
            return _cacheCalcedActionHandlerInfo.GetOrAdd(member,getHandlerInfo);
        }

        private static CalcedActionOAuthHandlerInfo getHandlerInfo(MemberInfo member)
        {
            var attributes = new List<WechatMPOAuthAttribute>();

            attributes.AddRange(member.GetCustomAttributes<WechatMPOAuthAttribute>(true));  //优先读取函数的attribute
            attributes.AddRange(member.DeclaringType.GetCustomAttributes<WechatMPOAuthAttribute>(true));

            var type = member.DeclaringType.BaseType;

            while (type != typeof(object))
            {
                var tmp = type.GetCustomAttributes<WechatMPOAuthAttribute>(true);

                if (tmp.HasData())
                {
                    attributes.AddRange(tmp);
                }

                type = type.BaseType;
            }

            var info=new CalcedActionOAuthHandlerInfo();

            var handlerTypeName = "";
            var scope = attributes.FirstOrDefault().Scope;
            var callbackUrl = "";

            for (int i = 0; i < attributes.Count; i++)
            {
                var item = attributes[i];

                if (string.IsNullOrWhiteSpace(handlerTypeName) && !string.IsNullOrWhiteSpace(item.HandleTypeName))
                {
                    handlerTypeName = item.HandleTypeName;
                }

                if (string.IsNullOrWhiteSpace(callbackUrl) && !string.IsNullOrWhiteSpace(item.CallbackUrl))
                {
                    callbackUrl = item.CallbackUrl;
                }

                if (!string.IsNullOrWhiteSpace(callbackUrl) && !string.IsNullOrWhiteSpace(callbackUrl))
                {
                    info.Member = member;
                    info.HanderTypeName = handlerTypeName;
                    info.CallbackUrl = callbackUrl;
                    info.Scope = scope;

                    return info;
                }
            }

            throw new ArgumentOutOfRangeException();
        }
    }

    internal class CalcedActionOAuthHandlerInfo
    {
        public MemberInfo Member { set; get; }

        public string HanderTypeName { set; get; }

        public string CallbackUrl { set; get; }

        public WechatOAuthScope Scope { set; get; }
    }
}